﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace Teal.Compiler {

    /// <summary>
    /// 表示一个词法解析器。
    /// </summary>
    [DebuggerDisplay("current = {current}, peek() = {peek()}")]
    public sealed class Lexer {

        #region 基本

        /// <summary>
        /// 用于词法解析的源。
        /// </summary>
        TextReader _input;

        /// <summary>
        /// 获取或设置用于词法解析的源。
        /// </summary>
        public TextReader input {
            get {
                return _input;
            }
            set {
                _input = value;

                // 清空数据。
                _actualLocation.column = 0;
                currentLocation.row = currentLocation.column = _actualLocation.row = 1;
                _ppDirectiveStack.Clear();

                // 清空当前标记，表示正在解析第一个标记。
                _currentToken = null;

                // 预读第一个标记。
                _currentChar = _input.Read();
                _scanTargetToken = new Token();
                scan();

                // 第一行默认是已换行。
                _scanTargetToken.hasLineTerminatorBeforeStart = true;

                // 初始化第一个标记。
                _currentToken = new Token();
                _currentToken.next = _scanTargetToken;

            }
        }

        /// <summary>
        /// 初始化 <see cref="Lexer"/> 类的新实例。
        /// </summary>
        public Lexer() { }

        /// <summary>
        /// 初始化 <see cref="Lexer"/> 类的新实例。
        /// </summary>
        /// <param name="input">词法解析的源。</param>
        public Lexer(TextReader input) {
            this.input = input;
        }

        /// <summary>
        /// Token 对象池。为了重用 Token 对象。
        /// </summary>
        Token[] _tokenPool = new Token[16];

        /// <summary>
        /// 存储 Token 对象池当前使用的位置。
        /// </summary>
        int _tokenPoolLength = 0;

        /// <summary>
        /// 存储当前标记。
        /// </summary>
        Token _currentToken;

        /// <summary>
        /// 获取当前的标记。
        /// </summary>
        public Token current {
            get {
                return _currentToken;
            }
        }

        /// <summary>
        /// 读取下一个标记。
        /// </summary>
        /// <returns>返回一个标记对象。</returns>
        public Token read() {

            Token nextToken = _currentToken.next;

            // 如果对象池还有空间，则将当前对象放入对象池。
            if (_tokenPoolLength < _tokenPool.Length) {
                _tokenPool[_tokenPoolLength++] = _currentToken;
                _currentToken.docComment = null;
                _currentToken.next = null;
            }

            // 如果下一位没有读取过，则进行读取 。
            if (nextToken.next == null) {

                // 如果对象池还有对象，则使用对象池的对象，否则创建新的对象。
                nextToken.next = _scanTargetToken = _tokenPoolLength > 0 ? _tokenPool[--_tokenPoolLength] : new Token();
                scan();
            }

            // 前进一位。
            return _currentToken = nextToken;
        }

        /// <summary>
        /// 预览下一个标记。
        /// </summary>
        /// <returns>返回一个标记对象。</returns>
        public Token peek() {
            return _currentToken.next;
        }

        /// <summary>
        /// 预览指定距离之后的标记。
        /// </summary>
        /// <returns>返回一个标记对象。</returns>
        public Token peek(int distance) {
            mark();
            while (distance-- > 0) {
                markRead();
            }
            return markRead();
        }

        /// <summary>
        /// 存储标记过的标记。
        /// </summary>
        Token _markedToken;

        /// <summary>
        /// 获取当前标记的位置所在的标记。
        /// </summary>
        public Token markCurrent {
            get {
                return _markedToken;
            }
        }

        /// <summary>
        /// 标记当前的位置。通过标记可支持标记的回溯。
        /// </summary>
        /// <remarks>
        /// 常规的 read() 和 peek() 操作是不可逆的操作。通过 mark() 以及 markRead() 和 markPeek() 可以提供对应的可逆版本。当用户执行一次 mark() 后，可以随意调用 markRead() 和 markPeek() 来获取之后的标记，但是这些操作不影响主流程的 read() 和 peek() 的返回值。 mark() 可以无限次调用。
        /// </remarks>
        public void mark() {
            _markedToken = _currentToken;
        }

        /// <summary>
        /// 从标记的位置开始，读取下一个字符。
        /// </summary>
        /// <returns></returns>
        public Token markRead() {

            // 读取一位。
            _markedToken = _markedToken.next;

            // 如果下一位没有读取过，则进行读取 。
            if (_markedToken.next == null) {

                // 如果对象池还有对象，则使用对象池的对象，否则创建新的对象。
                _markedToken.next = _tokenPoolLength > 0 ? _tokenPool[--_tokenPoolLength] : new Token();
                _scanTargetToken = _markedToken.next;
                scan();
            }

            return _markedToken;
        }

        /// <summary>
        /// 从标记的位置开始，预览下一个字符。
        /// </summary>
        /// <returns></returns>
        public Token markPeek() {
            return _markedToken.next;
        }

        #endregion

        #region 扫描

        /// <summary>
        /// 获取或设置记录词法解析的当前位置。
        /// </summary>
        public Location currentLocation;

        /// <summary>
        /// 当前文件的实际位置。主要用于标记 #line 之前的位置，仅当 column = 1 时值才有效。
        /// </summary>
        Location _actualLocation;

        /// <summary>
        /// 当解析到换行符之后，调用此函数进行一些初始化操作。
        /// </summary>
        private void newLine() {
            currentLocation.newLine();
            _scanTargetToken.hasLineTerminatorBeforeStart = true;

            // 如果当前位置和实际位置不同，则通知实际位置换行。
            if (_actualLocation.column == 1) {
                _actualLocation.newLine();
            }
        }

        /// <summary>
        /// 记录词法解析的当前字符。
        /// </summary>
        int _currentChar;

        /// <summary>
        /// 存储扫描结果存放的标记。
        /// </summary>
        Token _scanTargetToken;

        /// <summary>
        /// 从源中扫描下一个标记的数据并存放在 _scanTargetToken。
        /// </summary>
        private void scan() {

            Debug.Assert(_input != null, "应先设置“input”属性");

            // 重置换行的状态。
            _scanTargetToken.hasLineTerminatorBeforeStart = false;

        skip:

            #region 忽略空白字符

            switch (_currentChar) {
                case ' ':
                case '\t':
                    _currentChar = _input.Read();
                    currentLocation.column++;
                    goto skip;

                case '\r':
                    if (_input.Peek() == '\n') {
                        _input.Read();
                    }
                    goto case '\n';
                case '\n':
                    _currentChar = _input.Read();
                    newLine();
                    goto skip;

            }

            #endregion

            // 更新起始位置。
            _scanTargetToken.startLocation = currentLocation;

            #region 标识符和关键字

            // 其它大写字符在最后处理。
            if (_currentChar >= 'a' && _currentChar <= 'z') {

                StringBuilder buffer = _scanTargetToken.buffer;
                buffer.Length = 1;
                buffer[0] = (char)_currentChar;
                scanIdentifierBody();

                // 检查是否是前缀字符串或关键字。
                switch (buffer.Length) {

                    #region 关键字

                    // This region is generated by a tool. DO NOT EDIT!!!

                    #region 2

                    case 2:
                        switch (buffer[0]) {
                            case 'a':
                                if (buffer[1] == 's') {
                                    _scanTargetToken.type = TokenType.@as;
                                    goto end;
                                }
                                break;
                            case 'd':
                                if (buffer[1] == 'o') {
                                    _scanTargetToken.type = TokenType.@do;
                                    goto end;
                                }
                                break;
                            case 'i':
                                switch (buffer[1]) {
                                    case 'f':
                                        _scanTargetToken.type = TokenType.@if;
                                        goto end;
                                    case 's':
                                        _scanTargetToken.type = TokenType.@is;
                                        goto end;
                                }
                                break;
                            case 'o':
                                if (buffer[1] == 'r') {
                                    _scanTargetToken.type = TokenType.@or;
                                    goto end;
                                }
                                break;
                        }
                        break;

                    #endregion

                    #region 3

                    case 3:
                        switch (buffer[0]) {
                            case 'f':
                                if (buffer[1] == 'o' && buffer[2] == 'r') {
                                    _scanTargetToken.type = TokenType.@for;
                                    goto end;
                                }
                                break;
                            case 'i':
                                if (buffer[1] == 'n' && buffer[2] == 't') {
                                    _scanTargetToken.type = TokenType.@int;
                                    goto end;
                                }
                                break;
                            case 'n':
                                if (buffer[1] == 'e' && buffer[2] == 'w') {
                                    _scanTargetToken.type = TokenType.@new;
                                    goto end;
                                }
                                break;
                            case 'o':
                                if (buffer[1] == 'u' && buffer[2] == 't') {
                                    _scanTargetToken.type = TokenType.@out;
                                    goto end;
                                }
                                break;
                            case 'r':
                                if (buffer[1] == 'e' && buffer[2] == 'f') {
                                    _scanTargetToken.type = TokenType.@ref;
                                    goto end;
                                }
                                break;
                            case 't':
                                if (buffer[1] == 'r' && buffer[2] == 'y') {
                                    _scanTargetToken.type = TokenType.@try;
                                    goto end;
                                }
                                break;
                            case 'v':
                                if (buffer[1] == 'a' && buffer[2] == 'r') {
                                    _scanTargetToken.type = TokenType.@var;
                                    goto end;
                                }
                                break;
                        }
                        break;

                    #endregion

                    #region 4

                    case 4:
                        switch (buffer[0]) {
                            case 'b':
                                switch (buffer[1]) {
                                    case 'a':
                                        if (buffer[2] == 's' && buffer[3] == 'e') {
                                            _scanTargetToken.type = TokenType.@base;
                                            goto end;
                                        }
                                        break;
                                    case 'o':
                                        if (buffer[2] == 'o' && buffer[3] == 'l') {
                                            _scanTargetToken.type = TokenType.@bool;
                                            goto end;
                                        }
                                        break;
                                    case 'y':
                                        if (buffer[2] == 't' && buffer[3] == 'e') {
                                            _scanTargetToken.type = TokenType.@byte;
                                            goto end;
                                        }
                                        break;
                                }
                                break;
                            case 'c':
                                switch (buffer[1]) {
                                    case 'a':
                                        if (buffer[2] == 's' && buffer[3] == 'e') {
                                            _scanTargetToken.type = TokenType.@case;
                                            goto end;
                                        }
                                        break;
                                    case 'h':
                                        if (buffer[2] == 'a' && buffer[3] == 'r') {
                                            _scanTargetToken.type = TokenType.@char;
                                            goto end;
                                        }
                                        break;
                                }
                                break;
                            case 'e':
                                switch (buffer[1]) {
                                    case 'l':
                                        if (buffer[2] == 's' && buffer[3] == 'e') {
                                            _scanTargetToken.type = TokenType.@else;
                                            goto end;
                                        }
                                        break;
                                    case 'n':
                                        if (buffer[2] == 'u' && buffer[3] == 'm') {
                                            _scanTargetToken.type = TokenType.@enum;
                                            goto end;
                                        }
                                        break;
                                }
                                break;
                            case 'f':
                                if (buffer[1] == 'u' && buffer[2] == 'n' && buffer[3] == 'c') {
                                    _scanTargetToken.type = TokenType.func;
                                    goto end;
                                }
                                break;
                            case 'g':
                                if (buffer[1] == 'o' && buffer[2] == 't' && buffer[3] == 'o') {
                                    _scanTargetToken.type = TokenType.@goto;
                                    goto end;
                                }
                                break;
                            case 'l':
                                if (buffer[1] == 'o' && buffer[2] == 'n' && buffer[3] == 'g') {
                                    _scanTargetToken.type = TokenType.@long;
                                    goto end;
                                }
                                break;
                            case 'n':
                                if (buffer[1] == 'u' && buffer[2] == 'l' && buffer[3] == 'l') {
                                    _scanTargetToken.type = TokenType.@null;
                                    goto end;
                                }
                                break;
                            case 't':
                                switch (buffer[1]) {
                                    case 'h':
                                        if (buffer[2] == 'i' && buffer[3] == 's') {
                                            _scanTargetToken.type = TokenType.@this;
                                            goto end;
                                        }
                                        break;
                                    case 'r':
                                        if (buffer[2] == 'u' && buffer[3] == 'e') {
                                            _scanTargetToken.type = TokenType.@true;
                                            goto end;
                                        }
                                        break;
                                }
                                break;
                            case 'u':
                                if (buffer[1] == 'i' && buffer[2] == 'n' && buffer[3] == 't') {
                                    _scanTargetToken.type = TokenType.@uint;
                                    goto end;
                                }
                                break;
                            case 'v':
                                if (buffer[1] == 'o' && buffer[2] == 'i' && buffer[3] == 'd') {
                                    _scanTargetToken.type = TokenType.@void;
                                    goto end;
                                }
                                break;
                            case 'w':
                                if (buffer[1] == 'i' && buffer[2] == 't' && buffer[3] == 'h') {
                                    _scanTargetToken.type = TokenType.with;
                                    goto end;
                                }
                                break;
                        }
                        break;

                    #endregion

                    #region 5

                    case 5:
                        switch (buffer[0]) {
                            case 'b':
                                if (buffer[1] == 'r' && buffer[2] == 'e' && buffer[3] == 'a' && buffer[4] == 'k') {
                                    _scanTargetToken.type = TokenType.@break;
                                    goto end;
                                }
                                break;
                            case 'c':
                                switch (buffer[1]) {
                                    case 'a':
                                        if (buffer[2] == 't' && buffer[3] == 'c' && buffer[4] == 'h') {
                                            _scanTargetToken.type = TokenType.@catch;
                                            goto end;
                                        }
                                        break;
                                    case 'l':
                                        if (buffer[2] == 'a' && buffer[3] == 's' && buffer[4] == 's') {
                                            _scanTargetToken.type = TokenType.@class;
                                            goto end;
                                        }
                                        break;
                                    case 'o':
                                        if (buffer[2] == 'n' && buffer[3] == 's' && buffer[4] == 't') {
                                            _scanTargetToken.type = TokenType.@const;
                                            goto end;
                                        }
                                        break;
                                }
                                break;
                            case 'f':
                                switch (buffer[1]) {
                                    case 'a':
                                        if (buffer[2] == 'l' && buffer[3] == 's' && buffer[4] == 'e') {
                                            _scanTargetToken.type = TokenType.@false;
                                            goto end;
                                        }
                                        break;
                                    case 'i':
                                        if (buffer[2] == 'n' && buffer[3] == 'a' && buffer[4] == 'l') {
                                            _scanTargetToken.type = TokenType.final;
                                            goto end;
                                        }
                                        break;
                                    case 'l':
                                        if (buffer[2] == 'o' && buffer[3] == 'a' && buffer[4] == 't') {
                                            _scanTargetToken.type = TokenType.@float;
                                            goto end;
                                        }
                                        break;
                                }
                                break;
                            case 's':
                                if (buffer[1] == 'h' && buffer[2] == 'o' && buffer[3] == 'r' && buffer[4] == 't') {
                                    _scanTargetToken.type = TokenType.@short;
                                    goto end;
                                }
                                break;
                            case 't':
                                if (buffer[1] == 'h' && buffer[2] == 'r' && buffer[3] == 'o' && buffer[4] == 'w') {
                                    _scanTargetToken.type = TokenType.@throw;
                                    goto end;
                                }
                                break;
                            case 'u':
                                if (buffer[1] == 'l' && buffer[2] == 'o' && buffer[3] == 'n' && buffer[4] == 'g') {
                                    _scanTargetToken.type = TokenType.@ulong;
                                    goto end;
                                }
                                break;
                            case 'w':
                                if (buffer[1] == 'h' && buffer[2] == 'i' && buffer[3] == 'l' && buffer[4] == 'e') {
                                    _scanTargetToken.type = TokenType.@while;
                                    goto end;
                                }
                                break;
                            case 'y':
                                if (buffer[1] == 'i' && buffer[2] == 'e' && buffer[3] == 'l' && buffer[4] == 'd') {
                                    _scanTargetToken.type = TokenType.@yield;
                                    goto end;
                                }
                                break;
                        }
                        break;

                    #endregion

                    #region 6

                    case 6:
                        switch (buffer[0]) {
                            case 'd':
                                if (buffer[1] == 'o' && buffer[2] == 'u' && buffer[3] == 'b' && buffer[4] == 'l' && buffer[5] == 'e') {
                                    _scanTargetToken.type = TokenType.@double;
                                    goto end;
                                }
                                break;
                            case 'e':
                                switch (buffer[1]) {
                                    case 'x':
                                        switch (buffer[2]) {
                                            case 't':
                                                switch (buffer[3]) {
                                                    case 'e':
                                                        switch (buffer[4]) {
                                                            case 'n':
                                                                if (buffer[5] == 'd') {
                                                                    _scanTargetToken.type = TokenType.extend;
                                                                    goto end;
                                                                }
                                                                break;
                                                            case 'r':
                                                                if (buffer[5] == 'n') {
                                                                    _scanTargetToken.type = TokenType.@extern;
                                                                    goto end;
                                                                }
                                                                break;
                                                        }
                                                        break;
                                                }
                                                break;
                                        }
                                        break;
                                }
                                break;
                            case 'i':
                                if (buffer[1] == 'm' && buffer[2] == 'p' && buffer[3] == 'o' && buffer[4] == 'r' && buffer[5] == 't') {
                                    _scanTargetToken.type = TokenType.import;
                                    goto end;
                                }
                                break;
                            case 'o':
                                if (buffer[1] == 'b' && buffer[2] == 'j' && buffer[3] == 'e' && buffer[4] == 'c' && buffer[5] == 't') {
                                    _scanTargetToken.type = TokenType.@object;
                                    goto end;
                                }
                                break;
                            case 'p':
                                switch (buffer[1]) {
                                    case 'a':
                                        if (buffer[2] == 'r' && buffer[3] == 'a' && buffer[4] == 'm' && buffer[5] == 's') {
                                            _scanTargetToken.type = TokenType.@params;
                                            goto end;
                                        }
                                        break;
                                    case 'u':
                                        if (buffer[2] == 'b' && buffer[3] == 'l' && buffer[4] == 'i' && buffer[5] == 'c') {
                                            _scanTargetToken.type = TokenType.@public;
                                            goto end;
                                        }
                                        break;
                                }
                                break;
                            case 'r':
                                if (buffer[1] == 'e' && buffer[2] == 't' && buffer[3] == 'u' && buffer[4] == 'r' && buffer[5] == 'n') {
                                    _scanTargetToken.type = TokenType.@return;
                                    goto end;
                                }
                                break;
                            case 's':
                                switch (buffer[1]) {
                                    case 'i':
                                        if (buffer[2] == 'z' && buffer[3] == 'e' && buffer[4] == 'o' && buffer[5] == 'f') {
                                            _scanTargetToken.type = TokenType.@sizeof;
                                            goto end;
                                        }
                                        break;
                                    case 't':
                                        switch (buffer[2]) {
                                            case 'a':
                                                if (buffer[3] == 't' && buffer[4] == 'i' && buffer[5] == 'c') {
                                                    _scanTargetToken.type = TokenType.@static;
                                                    goto end;
                                                }
                                                break;
                                            case 'r':
                                                switch (buffer[3]) {
                                                    case 'i':
                                                        if (buffer[4] == 'n' && buffer[5] == 'g') {
                                                            _scanTargetToken.type = TokenType.@string;
                                                            goto end;
                                                        }
                                                        break;
                                                    case 'u':
                                                        if (buffer[4] == 'c' && buffer[5] == 't') {
                                                            _scanTargetToken.type = TokenType.@struct;
                                                            goto end;
                                                        }
                                                        break;
                                                }
                                                break;
                                        }
                                        break;
                                    case 'w':
                                        if (buffer[2] == 'i' && buffer[3] == 't' && buffer[4] == 'c' && buffer[5] == 'h') {
                                            _scanTargetToken.type = TokenType.@switch;
                                            goto end;
                                        }
                                        break;
                                }
                                break;
                            case 't':
                                if (buffer[1] == 'y' && buffer[2] == 'p' && buffer[3] == 'e' && buffer[4] == 'o' && buffer[5] == 'f') {
                                    _scanTargetToken.type = TokenType.@typeof;
                                    goto end;
                                }
                                break;
                            case 'u':
                                if (buffer[1] == 's' && buffer[2] == 'h' && buffer[3] == 'o' && buffer[4] == 'r' && buffer[5] == 't') {
                                    _scanTargetToken.type = TokenType.@ushort;
                                    goto end;
                                }
                                break;
                        }
                        break;

                    #endregion

                    #region 7

                    case 7:
                        switch (buffer[0]) {
                            case 'f':
                                if (buffer[1] == 'i' && buffer[2] == 'n' && buffer[3] == 'a' && buffer[4] == 'l' && buffer[5] == 'l' && buffer[6] == 'y') {
                                    _scanTargetToken.type = TokenType.@finally;
                                    goto end;
                                }
                                break;
                            case 'p':
                                if (buffer[1] == 'r' && buffer[2] == 'i' && buffer[3] == 'v' && buffer[4] == 'a' && buffer[5] == 't' && buffer[6] == 'e') {
                                    _scanTargetToken.type = TokenType.@private;
                                    goto end;
                                }
                                break;
                            case 'v':
                                if (buffer[1] == 'i' && buffer[2] == 'r' && buffer[3] == 't' && buffer[4] == 'u' && buffer[5] == 'a' && buffer[6] == 'l') {
                                    _scanTargetToken.type = TokenType.@virtual;
                                    goto end;
                                }
                                break;
                        }
                        break;

                    #endregion

                    #region 8

                    case 8:
                        switch (buffer[0]) {
                            case 'a':
                                if (buffer[1] == 'b' && buffer[2] == 's' && buffer[3] == 't' && buffer[4] == 'r' && buffer[5] == 'a' && buffer[6] == 'c' && buffer[7] == 't') {
                                    _scanTargetToken.type = TokenType.@abstract;
                                    goto end;
                                }
                                break;
                            case 'c':
                                if (buffer[1] == 'o' && buffer[2] == 'n' && buffer[3] == 't' && buffer[4] == 'i' && buffer[5] == 'n' && buffer[6] == 'u' && buffer[7] == 'e') {
                                    _scanTargetToken.type = TokenType.@continue;
                                    goto end;
                                }
                                break;
                            case 'o':
                                if (buffer[1] == 'v' && buffer[2] == 'e' && buffer[3] == 'r' && buffer[4] == 'r' && buffer[5] == 'i' && buffer[6] == 'd' && buffer[7] == 'e') {
                                    _scanTargetToken.type = TokenType.@override;
                                    goto end;
                                }
                                break;
                            case 'v':
                                if (buffer[1] == 'o' && buffer[2] == 'l' && buffer[3] == 'a' && buffer[4] == 't' && buffer[5] == 'i' && buffer[6] == 'l' && buffer[7] == 'e') {
                                    _scanTargetToken.type = TokenType.@volatile;
                                    goto end;
                                }
                                break;
                        }
                        break;

                    #endregion

                    #region 9

                    case 9:
                        switch (buffer[0]) {
                            case 'i':
                                if (buffer[1] == 'n' && buffer[2] == 't' && buffer[3] == 'e' && buffer[4] == 'r' && buffer[5] == 'f' && buffer[6] == 'a' && buffer[7] == 'c' && buffer[8] == 'e') {
                                    _scanTargetToken.type = TokenType.@interface;
                                    goto end;
                                }
                                break;
                            case 'n':
                                if (buffer[1] == 'a' && buffer[2] == 'm' && buffer[3] == 'e' && buffer[4] == 's' && buffer[5] == 'p' && buffer[6] == 'a' && buffer[7] == 'c' && buffer[8] == 'e') {
                                    _scanTargetToken.type = TokenType.@namespace;
                                    goto end;
                                }
                                break;
                            case 'p':
                                if (buffer[1] == 'r' && buffer[2] == 'o' && buffer[3] == 't' && buffer[4] == 'e' && buffer[5] == 'c' && buffer[6] == 't' && buffer[7] == 'e' && buffer[8] == 'd') {
                                    _scanTargetToken.type = TokenType.@protected;
                                    goto end;
                                }
                                break;
                        }
                        break;

                    #endregion


                    #endregion 关键字

                }

                // 其它未处理的情况都是普通标识符。
                _scanTargetToken.type = TokenType.identifier;
                goto end;

            }

            #endregion

            #region 数字

            if (Unicode.isDecimalDigit(_currentChar)) {

                _scanTargetToken.buffer.Length = 0;

                // 0x...
                if (_currentChar == '0' && (_input.Peek() | 0x20) == 'x') {

                    // 读取 0x
                    _input.Read();

                    // 读取十六进制。
                    while (Unicode.isHexDigit(_currentChar = _input.Read())) {
                        _scanTargetToken.buffer.Append((char)_currentChar);
                    }
                    currentLocation.column += 2 + _scanTargetToken.buffer.Length;

                    // 至少应读到一个数字。
                    if (_scanTargetToken.buffer.Length == 0) {
                        Compiler.error(ErrorCode.invalidHexNumber, "语法错误：无效的数字；应输入十六进制数字", _scanTargetToken.startLocation, currentLocation);
                    }

                    _scanTargetToken.type = TokenType.hexIntLiteral;
                    goto end;
                }

                // 整数部分。
                readDecimalDigits();

                // 小数部分。
                if (_currentChar == '.' && Unicode.isDecimalDigit(_input.Peek())) {
                    _currentChar = _input.Read();
                    scanFloatLiteral();
                    _scanTargetToken.type = TokenType.floatLiteral;
                    goto end;
                }

                currentLocation.column += _scanTargetToken.buffer.Length;
                _scanTargetToken.type = TokenType.intLiteral;
                goto end;

            }

            #endregion

            #region 操作符

            switch (_currentChar) {

                #region 运算符

                case '\'':
                case '"':
                    _scanTargetToken.type = TokenType.stringLiteral;
                    scanRegularString(_currentChar);
                    return;

                case '`':
                    _scanTargetToken.type = TokenType.stringLiteral;
                    scanVerbatimString();
                    goto end;

                // = == =>
                case '=':
                    switch (_currentChar = _input.Read()) {
                        case '=':
                            _scanTargetToken.type = TokenType.eq;
                            goto read2;
                        case '>':
                            _scanTargetToken.type = TokenType.assignTo;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.assign;
                            goto read1;
                    }

                // .1 .. ...
                case '.':
                    _currentChar = _input.Read();

                    // .1
                    if (Unicode.isDecimalDigit(_currentChar)) {
                        _scanTargetToken.type = TokenType.floatLiteral;
                        _scanTargetToken.buffer.Length = 0;
                        scanFloatLiteral();
                        goto end;
                    }

                    if (_currentChar == '.') {

                        if (_input.Peek() == '.') {
                            _input.Read();
                            _scanTargetToken.type = TokenType.ellipsis;
                            _currentChar = _input.Read();
                            currentLocation.column += 3;
                            goto end;
                        }

                        _scanTargetToken.type = TokenType.periodChain;
                        goto read2;
                    }

                    _scanTargetToken.type = TokenType.period;
                    goto read1;

                // /  // /* /=
                case '/':
                    switch (_currentChar = _input.Read()) {
                        case '/':

                            #region 文档注释

                            if (_input.Peek() == '/' && Compiler.options.docOutputPath != null) {

                                currentLocation.column += 2;

                                // 忽略开头空格。
                                _currentChar = _input.Read();
                                scanWhiteSpace();

                                if (_scanTargetToken.docComment == null) {
                                    _scanTargetToken.docComment = new DocComment();
                                }

                                // 读取标记。
                                _currentChar = _input.Read();
                                _scanTargetToken.buffer.Length = 0;
                                DocTag tag;

                                // 如果新行发现了新标记，则增加一个标记，
                                // 否则使用之前的标记。
                                if (_currentChar == '@') {
                                    currentLocation.column++;
                                    scanIdentifierBody();
                                    tag = _scanTargetToken.docComment.addTag(_scanTargetToken.buffer.ToString());
                                    _scanTargetToken.buffer.Length = 0;

                                    // 忽略标签名后面的空格。
                                    scanWhiteSpace();

                                } else {
                                    tag = _scanTargetToken.docComment.lastTag;
                                    if (tag.value != null) {
                                        _scanTargetToken.buffer.Append(tag.value);
                                        _scanTargetToken.buffer.Append('\n');
                                    }
                                }

                                tag.location = currentLocation;

                                // 读取当前行。
                                do {
                                    _scanTargetToken.buffer.Append((char)_currentChar);
                                } while (Unicode.isNonTerminator(_currentChar = _input.Read()));

                                // 删除行末空格。
                                int end = _scanTargetToken.buffer.Length;
                                while (--end >= 0 && Unicode.isWhiteSpace(_scanTargetToken.buffer[end])) ;
                                if (end > 0) {
                                    tag.value = _scanTargetToken.buffer.ToString(0, end);
                                }

                                goto skip;
                            }

                            #endregion

                            // 忽略注释。
                            skipLine();
                            goto skip;

                        case '*':
                            currentLocation.column += 2;

                        parseMultiLineComment:
                            switch (_currentChar = _input.Read()) {
                                case '*':

                                    // /* ... */
                                    if (_input.Peek() == '/') {
                                        _input.Read();
                                        _currentChar = _input.Read();
                                        currentLocation.column += 2;
                                        goto skip;
                                    }

                                    #region 文档注释

                                    if (Compiler.options.docOutputPath != null) {

                                        // /** */ 表示多行文档注释。
                                        // 多行文档注释内可能每行都包含一个 * 。
                                        // 如果行首发现 @，则新建标签，
                                        // 否则当前行直接属于上个标签。

                                        // 第一步：解析出所有文档注释内容。
                                        var startLocation = currentLocation;
                                        _scanTargetToken.buffer.Length = 0;

                                    parseMultiLineDocComment:
                                        switch (_currentChar = _input.Read()) {
                                            case '*':
                                                if (_input.Peek() == '/') {
                                                    _input.Read();
                                                    _currentChar = _input.Read();
                                                    currentLocation.column += 2;
                                                    break;
                                                }
                                                goto default;
                                            case '\r':
                                                if (_input.Peek() == '\n') {
                                                    _input.Read();
                                                }
                                                goto case '\n';
                                            case '\n':
                                                newLine();

                                                // 忽略第一个换行。
                                                if (_scanTargetToken.buffer.Length > 0) {
                                                    _scanTargetToken.buffer.Append('\n');
                                                } else {
                                                    startLocation.newLine();
                                                }

                                                // 忽略开头的空格和 *
                                            skipPrefixWhiteSpaceAndMark:

                                                // 发现新的空格，忽略。
                                                if (Unicode.isWhiteSpace(_input.Peek())) {
                                                    _input.Read();
                                                    currentLocation.column++;
                                                    goto skipPrefixWhiteSpaceAndMark;
                                                }

                                                // 发现 *，检测是否是注释结束，如果不是，则忽略。
                                                if (_input.Peek() == '*') {
                                                    _input.Read();
                                                    currentLocation.column++;
                                                    if (_input.Peek() == '/') {
                                                        goto case '*';
                                                    }
                                                    goto skipPrefixWhiteSpaceAndMark;
                                                }

                                                goto parseMultiLineDocComment;
                                            case -1:
                                                Compiler.error(ErrorCode.expectedMultiLineCommentEnd, "语法错误：多行注释未关闭；应输入“*/”", _scanTargetToken.startLocation, _scanTargetToken.startLocation + 2);
                                                break;
                                            default:
                                                currentLocation.column++;
                                                _scanTargetToken.buffer.Append((char)_currentChar);
                                                goto parseMultiLineDocComment;
                                        }

                                        // 第二步：解析文档注释。
                                        if (_scanTargetToken.buffer.Length > 0) {

                                            if (_scanTargetToken.docComment == null) {
                                                _scanTargetToken.docComment = new DocComment();
                                            }

                                            // _scanTargetToken 格式为：
                                            //     xxx
                                            //     @tag xxx
                                            //     @tag xxx

                                            DocTag tag = null;
                                            int lastIndex = 0;
                                            for (int i = 0; i < _scanTargetToken.buffer.Length; i++) {

                                                // 判断当前是否是 @，如果不是则 continue 。
                                                if (i > 0) {
                                                    if (_scanTargetToken.buffer[i] != '\n') {
                                                        continue;
                                                    }

                                                    startLocation.newLine();

                                                    // 发现新行，则解析 @标签。
                                                    if (++i >= _scanTargetToken.buffer.Length || _scanTargetToken.buffer[i] != '@') {
                                                        continue;
                                                    }
                                                } else if (_scanTargetToken.buffer[0] != '@') {
                                                    tag = _scanTargetToken.docComment.lastTag;
                                                    continue;
                                                }

                                                // 保存上一个标签。
                                                if (tag != null) {
                                                    tag.value = _scanTargetToken.buffer.ToString(lastIndex, i - lastIndex).Trim();
                                                }

                                                // 解析标签名。
                                                int left = ++i;
                                                while (Unicode.isIdentifierPart(_scanTargetToken.buffer[i])) {
                                                    i++;
                                                }
                                                tag = _scanTargetToken.docComment.addTag(_scanTargetToken.buffer.ToString(left, i - left));
                                                tag.location = startLocation;
                                                lastIndex = i;
                                            }

                                            // 保存最后一个标签。
                                            tag.value = _scanTargetToken.buffer.ToString(lastIndex, _scanTargetToken.buffer.Length - lastIndex).Trim();
                                        }

                                        goto skip;
                                    }

                                    goto default;

                                    #endregion

                                case '\r':
                                    if (_input.Peek() == '\n') {
                                        _input.Read();
                                    }
                                    goto case '\n';
                                case '\n':
                                    newLine();
                                    goto parseMultiLineComment;
                                case -1:
                                    Compiler.error(ErrorCode.expectedMultiLineCommentEnd, "语法错误：多行注释未关闭；应输入“*/”", _scanTargetToken.startLocation, _scanTargetToken.startLocation + 2);
                                    goto end;
                                default:
                                    currentLocation.column++;
                                    goto parseMultiLineComment;
                            }

                        case '=':
                            _scanTargetToken.type = TokenType.assignDiv;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.div;
                            goto read1;
                    }

                case '@':
                    _scanTargetToken.type = TokenType.at;
                    goto read0;

                case ';':
                    _scanTargetToken.type = TokenType.semicolon;
                    goto read0;

                case ',':
                    _scanTargetToken.type = TokenType.comma;
                    goto read0;

                case '(':
                    _scanTargetToken.type = TokenType.lParam;
                    goto read0;

                case ')':
                    _scanTargetToken.type = TokenType.rParam;
                    goto read0;

                case '[':
                    _scanTargetToken.type = TokenType.lBrack;
                    goto read0;

                case ']':
                    _scanTargetToken.type = TokenType.rBrack;
                    goto read0;

                case '{':
                    _scanTargetToken.type = TokenType.lBrace;
                    goto read0;

                case '}':
                    _scanTargetToken.type = TokenType.rBrace;
                    goto read0;

                // < <=
                case '<':
                    switch (_currentChar = _input.Read()) {
                        case '=':
                            _scanTargetToken.type = TokenType.lte;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.lt;
                            goto read1;
                    }

                // > >=
                case '>':
                    switch (_currentChar = _input.Read()) {
                        case '=':
                            _scanTargetToken.type = TokenType.gte;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.gt;
                            goto read1;
                    }

                // ! !=
                case '!':
                    switch (_currentChar = _input.Read()) {
                        case '=':
                            _scanTargetToken.type = TokenType.ne;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.not;
                            goto read1;
                    }

                // + ++ +=
                case '+':
                    switch (_currentChar = _input.Read()) {
                        case '+':
                            _scanTargetToken.type = TokenType.inc;
                            goto read2;
                        case '=':
                            _scanTargetToken.type = TokenType.assignAdd;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.add;
                            goto read1;
                    }

                // - -- -= ->
                case '-':
                    switch (_currentChar = _input.Read()) {
                        case '>':
                            _scanTargetToken.type = TokenType.lambda;
                            goto read2;
                        case '-':
                            _scanTargetToken.type = TokenType.dec;
                            goto read2;
                        case '=':
                            _scanTargetToken.type = TokenType.assignSub;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.sub;
                            goto read1;
                    }

                // * *=
                case '*':
                    switch (_currentChar = _input.Read()) {
                        case '=':
                            _scanTargetToken.type = TokenType.assignMul;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.mul;
                            goto read1;
                    }

                // % %= %& %| %~ %^ %< %>
                case '%':
                    switch (_currentChar = _input.Read()) {
                        case '=':
                            _scanTargetToken.type = TokenType.assignMod;
                            goto read2;
                        case '&':
                            _scanTargetToken.type = TokenType.bitAnd;
                            goto read2;
                        case '|':
                            _scanTargetToken.type = TokenType.bitOr;
                            goto read2;
                        case '~':
                            _scanTargetToken.type = TokenType.bitComplement;
                            goto read2;
                        case '<':
                            _scanTargetToken.type = TokenType.bitShl;
                            goto read2;
                        case '>':
                            _scanTargetToken.type = TokenType.bitShr;
                            goto read2;
                        case '^':
                            _scanTargetToken.type = TokenType.bitXor;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.mod;
                            goto read1;
                    }

                // & && &=
                case '&':
                    switch (_currentChar = _input.Read()) {
                        case '&':
                            _scanTargetToken.type = TokenType.logicalAnd;
                            goto read2;
                        case '=':
                            _scanTargetToken.type = TokenType.assignVarAnd;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.varAnd;
                            goto read1;
                    }

                // | || |=
                case '|':
                    switch (_currentChar = _input.Read()) {
                        case '|':
                            _scanTargetToken.type = TokenType.logicalOr;
                            goto read2;
                        case '=':
                            _scanTargetToken.type = TokenType.assignVarOr;
                            goto read2;
                        default:
                            _scanTargetToken.type = TokenType.varOr;
                            goto read1;
                    }

                case '?':
                    _scanTargetToken.type = TokenType.conditional;
                    goto read0;

                case ':':
                    _scanTargetToken.type = TokenType.colon;
                    goto read0;

                case '$':
                    _scanTargetToken.type = TokenType.identifier;
                    currentLocation.column++;
                    scanIdentifierBody();
                    goto end;

                case '^':
                    _scanTargetToken.type = TokenType.pow;
                    goto read0;

                case '~':
                    _scanTargetToken.type = TokenType.rangeTo;
                    goto read0;

                #endregion

                #region 预处理符

                case '#': {

                        currentLocation.column++;
                        StringBuilder buffer = _scanTargetToken.buffer;
                        buffer.Length = 0;
                        scanIdentifierBody();

                        // 预处理符必须单独一行。
                        // _currentToken == null 表示是第一个标记。
                        if (!_scanTargetToken.hasLineTerminatorBeforeStart && _currentToken != null) {
                            Compiler.error(ErrorCode.unexpectedPpDirective, "预处理符必须出现在行的最开头", _scanTargetToken.startLocation, currentLocation);
                        }

                        switch (buffer.Length) {
                            case 0:
                                // 忽略注释行。
                                skipLine();
                                goto skip;
                            case 2:
                                #region #if
                                if (buffer[0] == 'i' && buffer[1] == 'f') {
                                    if (parseAndEvalPpExpression()) {
                                        _ppDirectiveStack.Push(PpDirective.ifTrue);
                                        goto skip;
                                    }
                                    _ppDirectiveStack.Push(PpDirective.ifFalse);
                                    skipToPpElseOrEndIf();
                                    return;
                                }
                                goto default;
                                #endregion
                            case 3:
                                #region #sub
                                if (buffer[0] == 's' && buffer[1] == 'u' && buffer[2] == 'b') {

                                    // 无法内嵌 #sub
                                    if (_ppDirectiveStack.Contains(PpDirective.sub)) {
                                        Compiler.error(ErrorCode.unexpectedSubPpDirective, "无法在宏内嵌其它宏", _scanTargetToken.startLocation, currentLocation);
                                    }

                                    _ppDirectiveStack.Push(PpDirective.sub);

                                    // 读取宏名。
                                    switch (tryPpScan()) {
                                        case State.on:

                                            // 找到参数但是参数不是标识符。
                                            if (_scanTargetToken.type != TokenType.identifier) {
                                                reportIsNotIdentifierError();
                                                skipLine();
                                                goto case State.unset;
                                            }

                                            break;

                                        case State.off:
                                            Compiler.error(ErrorCode.expectedIdentifier, "语法错误：应输入标识符", currentLocation, currentLocation);
                                            goto case State.unset;

                                        case State.unset:
                                            goto skip;
                                    }

                                    // 检查宏名是否合法。
                                    string name = null;
                                    if (!_ppDirectiveStack.Contains(PpDirective.ifFalse)) {
                                        name = buffer.ToString();
                                        if (_subs == null) {
                                            _subs = new Dictionary<string, Token>();
                                        } else if (_subs.ContainsKey(name)) {
                                            Compiler.error(ErrorCode.dumpSub, String.Format("当前上下文已经存在名为“{0}”的宏", name), currentLocation - name.Length, currentLocation);
                                        }
                                    }

                                    // 读取参数。
                                    List<string> parameters = new List<string>();
                                    while (tryPpScan() == State.on) {

                                        // 参数必须是标识符。
                                        if (_scanTargetToken.type != TokenType.identifier) {
                                            reportIsNotIdentifierError();
                                            skipLine();
                                            break;
                                        }

                                        // 参数不能重复。
                                        string paramName = buffer.ToString();
                                        if (parameters.Contains(paramName)) {
                                            Compiler.error(ErrorCode.dumpSubParamName, String.Format("当前宏已经包含名为“{0}”的参数", paramName), _scanTargetToken.startLocation, _scanTargetToken.endLocation);
                                        } else {
                                            parameters.Add(paramName);
                                        }

                                        // 发现逗号，转到下一个参数。
                                        if (tryPpScan() == State.on && _scanTargetToken.type != TokenType.comma) {
                                            Compiler.error(ErrorCode.expectedSubParamComma, "语法错误：应输入“,”", _scanTargetToken.startLocation, _scanTargetToken.endLocation);
                                            skipLine();
                                            break;
                                        }

                                    }

                                    // 读取主体。

                                    // 不执行 if false 的宏。
                                    if (name == null) {
                                        goto skip;
                                    }

                                    // 先插入一个空的宏，防止解析宏时覆盖当前的宏。
                                    _subs[name] = null;

                                    Token currentToken = null;
                                    do {

                                        scan();

                                        // 直到 #endsub 结束。
                                        if (_ppDirectiveStack.Peek() == PpDirective.endSub) {
                                            _ppDirectiveStack.Pop();
                                            goto skip;
                                        }

                                        // 替换参数。
                                        Token token;
                                        if (_scanTargetToken.type == TokenType.identifier) {
                                            int argumentIndex = parameters.IndexOf(_scanTargetToken.buffer.ToString());
                                            if (argumentIndex >= 0) {
                                                token = new Token() {
                                                    type = TokenType.control,
                                                    tag = argumentIndex,
                                                    startLocation = _scanTargetToken.startLocation,
                                                    endLocation = _scanTargetToken.endLocation,
                                                    hasLineTerminatorBeforeStart = _scanTargetToken.hasLineTerminatorBeforeStart,
                                                };
                                            } else {
                                                token = _scanTargetToken.clone();
                                            }
                                        } else {
                                            token = _scanTargetToken.clone();
                                        }

                                        // 添加到链表。
                                        if (currentToken == null) {
                                            _subs[name] = currentToken = token;
                                        } else {
                                            currentToken = currentToken.next = token;
                                        }

                                    } while (_scanTargetToken.type != TokenType.eof);

                                    return;
                                }
                                goto default;
                                #endregion
                            case 4:
                                switch (buffer[0]) {
                                    case 'e':
                                        switch (buffer[1]) {
                                            case 'l':
                                                #region #else
                                                if (buffer[2] == 's' && buffer[3] == 'e') {
                                                    checkPpNonthingLeft("else");

                                                    switch (checkPpDirective(ErrorCode.unexpectedElsePpDirective, "else", PpDirective.ifTrue, PpDirective.ifFalse)) {

                                                        // #if true #else => #if false
                                                        case State.on:
                                                            _ppDirectiveStack.Push(PpDirective.ifFalse);
                                                            skipToPpElseOrEndIf();
                                                            return;

                                                        // #if false #else => #if true
                                                        case State.off:
                                                            _ppDirectiveStack.Push(PpDirective.elseIfTrue);
                                                            return;

                                                        default:
                                                            goto skip;
                                                    }

                                                }
                                                break;
                                                #endregion
                                            case 'c':
                                                #region #echo
                                                if (buffer[2] == 'h' && buffer[3] == 'o') {
                                                    scanPpArgument();
                                                    Compiler.echo(buffer.ToString(), _scanTargetToken.startLocation, currentLocation);
                                                    goto skip;
                                                }
                                                break;
                                                #endregion
                                        }
                                        break;
                                    case 'l':
                                        #region #line
                                        if (buffer[1] == 'i' && buffer[2] == 'n' && buffer[3] == 'e') {
                                            if (_ppDirectiveStack.Contains(PpDirective.sub)) {
                                                Compiler.error(ErrorCode.unexpectedLinePpDirective, "无法在宏内部使用 #line 指令", _scanTargetToken.startLocation, currentLocation);
                                            }

                                            // 忽略空白。
                                            scanWhiteSpace();
                                            _scanTargetToken.startLocation = currentLocation;

                                            #region #line 行号

                                            if (Unicode.isDecimalDigit(_currentChar)) {

                                                // 读取行号。
                                                int lineNumber = _currentChar - '0';
                                                currentLocation.column++;
                                            parseNextLineNumber:
                                                if (Unicode.isDecimalDigit(_currentChar = _input.Read())) {
                                                    currentLocation.column++;
                                                    int val = _currentChar - '0';
                                                    if (val >= 0 && val <= 9) {
                                                        try {
                                                            lineNumber = checked(lineNumber * 10 + val);
                                                        } catch (OverflowException) {
                                                            Compiler.error(ErrorCode.invalidLineNumber, "指定的行号太大", _scanTargetToken.startLocation, currentLocation);
                                                            goto skip;
                                                        }
                                                        goto parseNextLineNumber;
                                                    }
                                                }

                                                // 读取文件路径。
                                                scanWhiteSpace();
                                                if (_currentChar == ',') {
                                                    _currentChar = _input.Read();
                                                    scanWhiteSpace();
                                                }

                                                buffer.Length = 0;
                                                scanLine();

                                                // #if false 处不执行表达式。
                                                if (!_ppDirectiveStack.Contains(PpDirective.ifFalse)) {

                                                    // 备份原位置，用于之后 #line default 时恢复路径。
                                                    if (_actualLocation.column == 0) {
                                                        _actualLocation = currentLocation;
                                                        _actualLocation.column = 1;
                                                    }

                                                    // 设置新的位置。
                                                    currentLocation = new Location(buffer.Length == 0 ? currentLocation.sourcePath : buffer.ToString(), lineNumber - 1);

                                                }

                                                goto skip;

                                            }

                                            #endregion

                                            switch (_currentChar) {
                                                case 'h':
                                                    #region #line hidden
                                                    if ((_currentChar = _input.Read()) == 'i' && (_currentChar = _input.Read()) == 'd' && (_currentChar = _input.Read()) == 'd' && (_currentChar = _input.Read()) == 'e' && (_currentChar = _input.Read()) == 'n' && !Unicode.isIdentifierPart(_currentChar = _input.Read())) {
                                                        // #if false 处不执行表达式。
                                                        if (!_ppDirectiveStack.Contains(PpDirective.ifFalse)) {

                                                            // 备份原位置，用于之后 #line default 时恢复路径。
                                                            if (_actualLocation.column == 0) {
                                                                _actualLocation = currentLocation;
                                                                _actualLocation.column = 1;
                                                            }

                                                            currentLocation.row--;
                                                            currentLocation.column = _scanTargetToken.startLocation.column + 6;
                                                        }
                                                        checkPpNonthingLeft("line hidden");
                                                        goto skip;
                                                    }
                                                    goto default;
                                                    #endregion
                                                case 'd':
                                                    #region #line default
                                                    if ((_currentChar = _input.Read()) == 'e' && (_currentChar = _input.Read()) == 'f' && (_currentChar = _input.Read()) == 'a' && (_currentChar = _input.Read()) == 'u' && (_currentChar = _input.Read()) == 'l' && (_currentChar = _input.Read()) == 't' && !Unicode.isIdentifierPart(_currentChar = _input.Read())) {
                                                        // #if false 处不执行表达式。
                                                        if (!_ppDirectiveStack.Contains(PpDirective.ifFalse)) {
                                                            currentLocation = _actualLocation;
                                                            _actualLocation.column = 0;
                                                            currentLocation.column = _scanTargetToken.startLocation.column + 7;
                                                        }
                                                        checkPpNonthingLeft("line default");
                                                        goto skip;
                                                    }
                                                    goto default;
                                                    #endregion
                                                default:
                                                    skipLine();
                                                    Compiler.error(ErrorCode.invalidLineDirectiveParam, "语法错误：应输入行号、“default”或“hidden”", _scanTargetToken.startLocation, currentLocation);
                                                    goto skip;
                                            }
                                        }
                                        break;
                                        #endregion
                                    case 't':
                                        #region #todo
                                        if (buffer[1] == 'o' && buffer[2] == 'd' && buffer[3] == 'o') {
                                            scanPpArgument();
                                            Compiler.warning(ErrorCode.todo, buffer.ToString(), _scanTargetToken.startLocation, currentLocation);
                                            goto skip;
                                        }
                                        break;
                                        #endregion
                                    case 'n':
                                        #region #note
                                        if (buffer[1] == 'o' && buffer[2] == 't' && buffer[3] == 'e') {
                                            skipLine();
                                            goto skip;
                                        }
                                        break;
                                        #endregion
                                    case 'm':
                                        #region #mark
                                        if (buffer[1] == 'a' && buffer[2] == 'r' && buffer[3] == 'k') {
                                            skipLine();
                                            goto skip;
                                        }
                                        break;
                                        #endregion
                                }
                                goto default;
                            case 5:
                                switch (buffer[0]) {
                                    case 'e':
                                        switch (buffer[1]) {
                                            case 'r':
                                                #region #error
                                                if (buffer[2] == 'r' && buffer[3] == 'o' && buffer[4] == 'r') {
                                                    scanPpArgument();
                                                    Compiler.error(ErrorCode.userDefinedError, buffer.ToString(), _scanTargetToken.startLocation, currentLocation);
                                                    goto skip;
                                                }
                                                break;
                                                #endregion
                                            case 'n':
                                                #region #endif
                                                if (buffer[2] == 'd' && buffer[3] == 'i' && buffer[4] == 'f') {
                                                    checkPpNonthingLeft("endif");

                                                    if (checkPpDirective(ErrorCode.unexpectedEndifPpDirective, "endif", PpDirective.ifTrue, PpDirective.ifFalse) != State.off) {
                                                        goto skip;
                                                    }

                                                    // #if false #endif
                                                    _ppDirectiveStack.Push(PpDirective.endIf);
                                                    return;

                                                }
                                                break;
                                                #endregion
                                        }
                                        break;
                                    case 't':
                                        #region #tofix
                                        if (buffer[1] == 'o' && buffer[2] == 'f' && buffer[3] == 'i' && buffer[4] == 'x') {
                                            scanPpArgument();
                                            Compiler.warning(ErrorCode.tofix, buffer.ToString(), _scanTargetToken.startLocation, currentLocation);
                                            goto skip;
                                        }
                                        break;
                                        #endregion
                                }
                                goto default;
                            case 6:
                                switch (buffer[0]) {
                                    case 'e':
                                        switch (buffer[1]) {
                                            case 'n':
                                                #region #endsub
                                                if (buffer[2] == 'd' && buffer[3] == 's' && buffer[4] == 'u' && buffer[5] == 'b') {
                                                    checkPpNonthingLeft("endsub");
                                                    if (checkPpDirective(ErrorCode.unexpectedEndSubDirective, "endsub", PpDirective.sub, PpDirective.sub) != State.on) {
                                                        goto skip;
                                                    }
                                                    _ppDirectiveStack.Push(PpDirective.endSub);
                                                    return;
                                                }
                                                break;
                                                #endregion
                                            case 'l':
                                                #region #elseif
                                                if (buffer[2] == 's' && buffer[3] == 'e' && buffer[4] == 'i' && buffer[5] == 'f') {

                                                    switch (checkPpDirective(ErrorCode.unexpectedElseIfPpDirective, "elseif", PpDirective.ifTrue, PpDirective.ifFalse)) {

                                                        // #if true #elseif => #if false
                                                        case State.on:
                                                            skipLine();
                                                            _ppDirectiveStack.Push(PpDirective.ifFalse);
                                                            skipToPpElseOrEndIf();
                                                            return;

                                                        // #if false #elseif => #if true
                                                        case State.off:

                                                            // 通知 skipToElseOrEndIf() 停止。
                                                            _ppDirectiveStack.Push(parseAndEvalPpExpression() ? PpDirective.elseIfTrue : PpDirective.elseIfFalse);
                                                            return;

                                                        default:
                                                            skipLine();
                                                            goto skip;
                                                    }

                                                }
                                                break;
                                                #endregion
                                        }
                                        break;
                                    case 'r':
                                        #region #region
                                        if (buffer[1] == 'e' && buffer[2] == 'g' && buffer[3] == 'i' && buffer[4] == 'o' && buffer[5] == 'n') {
                                            skipLine();
                                            _ppDirectiveStack.Push(PpDirective.region);
                                            goto skip;
                                        }
                                        break;
                                        #endregion
                                    case 'p':
                                        #region #pragma
                                        if (buffer[1] == 'r' && buffer[2] == 'a' && buffer[3] == 'g' && buffer[4] == 'm' && buffer[5] == 'a') {

                                            skipLine();

                                            // 应用配置。
                                            // #if false 处不执行表达式。
                                            if (!_ppDirectiveStack.Contains(PpDirective.ifFalse)) {
                                                // todo
                                            }

                                            goto skip;
                                        }
                                        break;
                                        #endregion
                                    case 'u':
                                        #region #usesub
                                        if (buffer[1] == 's' && buffer[2] == 'e' && buffer[3] == 's' && buffer[4] == 'u' && buffer[5] == 'b') {

                                            #region 读取宏名

                                            switch (tryPpScan()) {
                                                case State.on:
                                                    if (_scanTargetToken.type != TokenType.identifier) {
                                                        reportIsNotIdentifierError();
                                                        skipLine();
                                                        goto case State.unset;
                                                    }
                                                    break;

                                                case State.off:
                                                    Compiler.error(ErrorCode.expectedIdentifier, "语法错误：应输入标识符", currentLocation, currentLocation);
                                                    goto case State.unset;

                                                case State.unset:
                                                    goto skip;
                                            }

                                            // 判断宏是否定义。
                                            string name = buffer.ToString();
                                            Token sub;
                                            if (_subs == null || !_subs.TryGetValue(name, out sub)) {
                                                Compiler.error(ErrorCode.subNotFound, String.Format("当前上下文不存在名为“{0}”的宏", name), _scanTargetToken.startLocation, _scanTargetToken.endLocation);
                                                skipLine();
                                                goto skip;
                                            }

                                            #endregion

                                            #region 读取参数

                                            List<Token> arguments = ListObjectPool<Token>.alloc();

                                            Token currentArgument = null;
                                            Token currentToken = null;
                                            while (tryPpScan() == State.on) {

                                                // 发现逗号，则参数添加到列表。
                                                if (_scanTargetToken.type == TokenType.comma) {
                                                    arguments.Add(currentArgument);
                                                    currentArgument = null;
                                                    continue;
                                                }

                                                Token token = _scanTargetToken.clone();

                                                if (currentArgument == null) {
                                                    currentToken = currentArgument = token;
                                                } else {
                                                    currentToken = currentToken.next = token;
                                                }

                                            }

                                            if (currentArgument != null) {
                                                arguments.Add(currentArgument);
                                            }

                                            #endregion

                                            #region 输出内容

                                            Token newTokenStart = null;

                                            for (Token c = sub; c != null; c = c.next) {

                                                #region 插入参数

                                                if (c.type == TokenType.control) {
                                                    int argumentIndex = (int)c.tag;

                                                    if (argumentIndex < arguments.Count) {
                                                        for (Token argument = arguments[argumentIndex]; argument != null; argument = argument.next) {
                                                            if (newTokenStart == null) {
                                                                currentToken = newTokenStart = argument.clone();
                                                            } else {
                                                                currentToken = currentToken.next = argument.clone();
                                                            }
                                                        }
                                                    }

                                                    continue;

                                                }

                                                #endregion

                                                // 非参数直接追加到原标记末尾。
                                                if (newTokenStart == null) {
                                                    currentToken = newTokenStart = c.clone();
                                                } else {
                                                    currentToken = currentToken.next = c.clone();
                                                }

                                            }

                                            ListObjectPool<Token>.free(arguments);

                                            // 如果只能替换成空的标记，则直接忽略此标记。
                                            if (newTokenStart == null) {
                                                goto skip;
                                            }

                                            // 由于无法修改 _scanTargetToken，只能复制处理。
                                            _scanTargetToken.type = newTokenStart.type;
                                            _scanTargetToken.startLocation = newTokenStart.startLocation;
                                            _scanTargetToken.endLocation = newTokenStart.endLocation;
                                            _scanTargetToken.buffer = newTokenStart.buffer;
                                            _scanTargetToken.hasLineTerminatorBeforeStart = newTokenStart.hasLineTerminatorBeforeStart;
                                            _scanTargetToken.docComment = newTokenStart.docComment;
                                            _scanTargetToken.next = newTokenStart.next;

                                            return;

                                            #endregion

                                        }
                                        break;
                                        #endregion
                                }
                                goto default;
                            case 7:
                                #region #warning
                                if (buffer[0] == 'w' && buffer[1] == 'a' && buffer[2] == 'r' && buffer[3] == 'n' && buffer[4] == 'i' && buffer[5] == 'n' && buffer[6] == 'g') {
                                    scanPpArgument();
                                    Compiler.warning(ErrorCode.userDefinedWarning, buffer.ToString(), _scanTargetToken.startLocation, currentLocation);
                                    goto skip;
                                }
                                goto default;
                                #endregion
                            case 9:
                                #region #endregion
                                if (buffer[0] == 'e' && buffer[1] == 'n' && buffer[2] == 'd' && buffer[3] == 'r' && buffer[4] == 'e' && buffer[5] == 'g' && buffer[6] == 'i' && buffer[7] == 'o' && buffer[8] == 'n') {
                                    skipLine();
                                    checkPpDirective(ErrorCode.unexpectedEndRegionPpDirective, "endregion", PpDirective.region, PpDirective.region);
                                    goto skip;
                                }
                                goto default;
                                #endregion
                            default:

                                // 未知的指令。
                                Compiler.error(ErrorCode.invalidPpDirective, String.Format("未知的预处理符“{0}”", buffer.ToString()), _scanTargetToken.startLocation, currentLocation);
                                skipLine();
                                goto skip;

                        }

                    }

                #endregion

                #region 其它符号

                case -1:

                    // 确认所有命令都已关闭
                    if (_scanTargetToken.type != TokenType.eof && _ppDirectiveStack.Count > 0) {
                        switch (_ppDirectiveStack.Peek()) {
                            case PpDirective.sub:
                                Compiler.error(ErrorCode.expectedEndSubDirective, "#sub 指令未关闭；应输入“#endsub”", currentLocation, currentLocation);
                                break;
                            case PpDirective.region:
                                Compiler.error(ErrorCode.expectedEndRegionPpDirective, "#region 指令未关闭；应输入“#endregion”", currentLocation, currentLocation);
                                break;
                            default:
                                Compiler.error(ErrorCode.expectedEndIfPpDirective, "#if 指令未关闭；应输入“#endif”", currentLocation, currentLocation);
                                break;
                        }
                    }
                    _scanTargetToken.type = TokenType.eof;
                    goto end;

                default:

                    if (Unicode.isLetter(_currentChar) || _currentChar == '_') {
                        _scanTargetToken.type = TokenType.identifier;
                        _scanTargetToken.buffer.Length = 1;
                        _scanTargetToken.buffer[0] = (char)_currentChar;
                        scanIdentifierBody();
                        goto end;
                    }

                    if (Unicode.isWhiteSpace(_currentChar)) {
                        _currentChar = _input.Read();
                        currentLocation.column++;
                        goto skip;
                    }

                    if (Unicode.isLineTerminator(_currentChar)) {
                        _currentChar = _input.Read();
                        newLine();
                        goto skip;
                    }

                    #region 全角符

                    //此部分字符不是语音规范的一部分。

                    switch (_currentChar) {

                        case '。':
                            _scanTargetToken.type = TokenType.period;
                            goto read0;

                        case '，':
                            _scanTargetToken.type = TokenType.comma;
                            goto read0;

                        case '：':
                            _scanTargetToken.type = TokenType.colon;
                            goto read0;

                        case '；':
                            _scanTargetToken.type = TokenType.semicolon;
                            goto read0;

                        case '？':
                            _scanTargetToken.type = TokenType.conditional;
                            goto read0;

                        case '＝':
                            _scanTargetToken.type = TokenType.assign;
                            goto read0;

                        case '≡':
                            _scanTargetToken.type = TokenType.eq;
                            goto read0;

                        case '≠':
                            _scanTargetToken.type = TokenType.ne;
                            goto read0;

                        case '＜':
                            _scanTargetToken.type = TokenType.lt;
                            goto read0;

                        case '＞':
                            _scanTargetToken.type = TokenType.gt;
                            goto read0;

                        case '≤':
                            _scanTargetToken.type = TokenType.lte;
                            goto read0;

                        case '≥':
                            _scanTargetToken.type = TokenType.gte;
                            goto read0;

                        case '﹢':
                            _scanTargetToken.type = TokenType.add;
                            goto read0;

                        case '﹣':
                            _scanTargetToken.type = TokenType.sub;
                            goto read0;

                        case '×':
                            _scanTargetToken.type = TokenType.mul;
                            goto read0;

                        case '÷':
                            _scanTargetToken.type = TokenType.div;
                            goto read0;

                        case '﹙':
                            _scanTargetToken.type = TokenType.lParam;
                            goto read0;

                        case '﹚':
                            _scanTargetToken.type = TokenType.rParam;
                            goto read0;

                        case '[':
                            _scanTargetToken.type = TokenType.lBrack;
                            goto read0;

                        case ']':
                            _scanTargetToken.type = TokenType.rBrack;
                            goto read0;

                        case '﹛':
                            _scanTargetToken.type = TokenType.lBrace;
                            goto read0;

                        case '﹜':
                            _scanTargetToken.type = TokenType.rBrace;
                            goto read0;

                        case '＆':
                            _scanTargetToken.type = TokenType.logicalAnd;
                            goto read0;

                        case '｜':
                            _scanTargetToken.type = TokenType.logicalOr;
                            goto read0;

                        case '％':
                            _scanTargetToken.type = TokenType.mod;
                            goto read0;

                        case '！':
                            _scanTargetToken.type = TokenType.not;
                            goto read0;

                        case '→':
                            _scanTargetToken.type = TokenType.lambda;
                            goto read0;

                        case '‘':
                            _scanTargetToken.type = TokenType.stringLiteral;
                            scanRegularString('’');
                            return;

                        case '“':
                            _scanTargetToken.type = TokenType.stringLiteral;
                            scanRegularString('”');
                            return;
                    }

                    #endregion

                    // 剩下的字符为不支持的字符。
                    Compiler.error(ErrorCode.unexpectedChar, String.Format("语法错误：意外的字符：“{0}”", (char)_currentChar), _scanTargetToken.startLocation, currentLocation);

                    // 忽略此字符。
                    _currentChar = _input.Read();
                    currentLocation.column++;
                    goto skip;

                #endregion

            }

            #endregion

        read0:
            _currentChar = _input.Read();

        read1:
            currentLocation.column++;
            goto end;

        read2:
            _currentChar = _input.Read();
            currentLocation.column += 2;

        end:

            // 更新结束位置。
            _scanTargetToken.endLocation = currentLocation;

        }

        /// <summary>
        /// 扫描紧跟的标识符主体部分。忽略 _currentChar 值。
        /// </summary>
        private void scanIdentifierBody() {
            while (Unicode.isIdentifierPart(_currentChar = _input.Read())) {
                _scanTargetToken.buffer.Append((char)_currentChar);
            }
            currentLocation.column += _scanTargetToken.buffer.Length;
        }

        /// <summary>
        /// 用于标记当前正在处理的字符串内置表达式。如果值为 0，说明未在处理字符串表达式。如果值为 -1，说明禁止处理字符串表达式。否则，值为正在处理的表达式所在字符串的引号字符。
        /// </summary>
        int _parsingStringVarableChar;

        /// <summary>
        /// 扫描紧跟的转义字符串部分。忽略 _currentChar 值。
        /// </summary>
        private void scanRegularString(int currentChar) {

            // 在解析字符串变量时不能再次解析到字符串。
            if (_parsingStringVarableChar == currentChar) {
                Compiler.error(ErrorCode.expectedStringLiteralExpressionEnd, "语法错误：字符串内联表达式未关闭；应输入“}”", currentLocation, currentLocation + 1);
                _scanTargetToken.type = TokenType.rBrace;
                return;
            }

            currentLocation.column++; // 引号。

            // 用于读取变量表达式时存储原始标记。
            Token orignalScanTargetToken = _scanTargetToken;

            _scanTargetToken.buffer.Length = 0;
            while (true) {
                _currentChar = _input.Read();
            parseCurrent:
                switch (_currentChar) {
                    case '"':
                    case '\'':
                        // 允许在 "" 字符串中不转义 ' 字符。反之亦然。
                        if (_currentChar != currentChar) {
                            goto default;
                        }

                        _currentChar = _input.Read();
                        currentLocation.column++;
                        goto end;

                    #region 转义
                    case '\\':
                        switch (_currentChar = _input.Read()) {
                            case 'n':
                                _currentChar = '\n';
                                goto default;
                            case 'r':
                                _currentChar = '\r';
                                goto default;
                            case 't':
                                _currentChar = '\t';
                                goto default;
                            case 'u':
                                _currentChar = readUnicodeChar(4);
                                goto default;
                            case 'x':
                                _currentChar = readUnicodeChar(2);
                                goto default;
                            case '\r':
                                if (_input.Peek() == '\n') {
                                    _input.Read();
                                }
                                goto case '\n';
                            case '\n':
                                newLine();
                                continue;
                            case 'b':
                                _currentChar = '\b';
                                goto default;
                            case 'f':
                                _currentChar = '\f';
                                goto default;
                            case 'v':
                                _currentChar = '\v';
                                goto default;
                            case '0':
                                _currentChar = '\0';
                                goto default;
                            case -1:
                                orignalScanTargetToken.buffer.Append('\\');
                                currentLocation.column++;
                                continue;
                            default:
                                orignalScanTargetToken.buffer.Append((char)_currentChar);
                                currentLocation.column += 2;
                                continue;
                        }
                    #endregion

                    case '\r':
                        _scanTargetToken.buffer.Append('\r');
                        if (_input.Peek() == '\n') {
                            _input.Read();
                            goto case '\n';
                        }
                        newLine();
                        continue;
                    case '\n':
                        _scanTargetToken.buffer.Append('\n');
                        newLine();
                        continue;
                    case -1:
                        Compiler.error(ErrorCode.expectedStringLiteralEnd, String.Format("语法错误：字符串未关闭；应输入“{0}”", (char)currentChar), _scanTargetToken.startLocation, currentLocation);
                        goto end;

                    #region 变量字符串
                    case '$':

                        // $xxx, ${xx}
                        if (_parsingStringVarableChar >= 0 && (Unicode.isIdentifierPart(_input.Peek()) || _input.Peek() == '{')) {

                            // 标记之前的字符串已读取完毕。
                            _scanTargetToken.endLocation = currentLocation;

                            // 第一次发现变量，生成 ("之前的字符串" + <表达式> + "之后的字符串") 标记序列。
                            // 在 _scanTargetToken 后插入变量。
                            if (orignalScanTargetToken == _scanTargetToken) {
                                _scanTargetToken.next = _scanTargetToken.clone();
                                _scanTargetToken.type = TokenType.lParam;
                                _scanTargetToken.endLocation = _scanTargetToken.startLocation + 1;
                                _scanTargetToken = _scanTargetToken.next;
                                _scanTargetToken.startLocation++;
                            }

                            // 插入 + 。
                            _scanTargetToken = _scanTargetToken.next = new Token() {
                                type = TokenType.add,
                                startLocation = currentLocation,
                                endLocation = currentLocation,
                            };

                            // $abc
                            if (_input.Peek() != '{') {

                                // 插入字符串。
                                _scanTargetToken = _scanTargetToken.next = new Token();
                                scan();

                                // 插入 +。
                                _scanTargetToken = _scanTargetToken.next = new Token() {
                                    type = TokenType.add,
                                    startLocation = currentLocation,
                                    endLocation = currentLocation,
                                };

                            } else {

                                // 插入 (
                                _scanTargetToken = _scanTargetToken.next = new Token() {
                                    type = TokenType.lParam,
                                    startLocation = currentLocation + 1,
                                    endLocation = currentLocation + 2,
                                };

                                // 读取 ${
                                _input.Read(); // {
                                _currentChar = _input.Read();
                                currentLocation.column += 2;

                                bool foundSomething = false;
                                _parsingStringVarableChar = currentChar;

                                // 插入 } 之前的所有标记。
                                while (true) {
                                    _scanTargetToken = _scanTargetToken.next = new Token();
                                    scan();
                                    if (_scanTargetToken.type == TokenType.rBrace) {
                                        break;
                                    }
                                    if (_scanTargetToken.type == TokenType.eof) {
                                        goto end;
                                    }
                                    foundSomething = true;
                                }

                                _parsingStringVarableChar = 0;

                                if (!foundSomething) {
                                    _scanTargetToken.type = TokenType.@null;

                                    // 插入 )
                                    _scanTargetToken = _scanTargetToken.next = new Token() {
                                        type = TokenType.rParam,
                                        startLocation = currentLocation - 1,
                                        endLocation = currentLocation
                                    };

                                } else {
                                    // 插入 )
                                    _scanTargetToken.type = TokenType.rParam;
                                }

                                // 插入 + 。
                                _scanTargetToken = _scanTargetToken.next = new Token() {
                                    type = TokenType.add,
                                    startLocation = _scanTargetToken.startLocation,
                                    endLocation = _scanTargetToken.startLocation
                                };

                            }

                            // 插入后续字符串。
                            _scanTargetToken = _scanTargetToken.next = new Token() {
                                type = TokenType.stringLiteral,
                                startLocation = currentLocation,
                            };
                            goto parseCurrent;

                        }

                        goto default;
                    #endregion

                    default:
                        _scanTargetToken.buffer.Append((char)_currentChar);
                        currentLocation.column++;
                        continue;

                }
            }

        end:

            // 如果之前解析了变量字符串，则重定位开头。
            if (orignalScanTargetToken != _scanTargetToken) {
                _scanTargetToken.endLocation = currentLocation - 1;
                _scanTargetToken.next = new Token() {
                    type = TokenType.rParam,
                    startLocation = currentLocation - 1,
                    endLocation = currentLocation,
                };
                _scanTargetToken = orignalScanTargetToken;
                return;
            }

            orignalScanTargetToken.endLocation = currentLocation;
        }

        /// <summary>
        /// 扫描紧跟的无转义字符串部分。忽略 _currentChar 值。
        /// </summary>
        private void scanVerbatimString() {

            // 读取引号。
            currentLocation.column++;

            _scanTargetToken.buffer.Length = 0;
            while (true) {
                switch (_currentChar = _input.Read()) {
                    case '`':
                        _currentChar = _input.Read();
                        currentLocation.column++;
                        if (_currentChar == '`') {
                            goto default;
                        }
                        return;
                    case '\r':
                        _scanTargetToken.buffer.Append('\r');
                        if (_input.Peek() == '\n') {
                            _input.Read();
                            goto case '\n';
                        }
                        newLine();
                        continue;
                    case '\n':
                        _scanTargetToken.buffer.Append('\n');
                        newLine();
                        continue;
                    case -1:
                        _scanTargetToken.endLocation = currentLocation;
                        Compiler.error(ErrorCode.expectedStringLiteralEnd, String.Format("语法错误：字符串未关闭；应输入“{0}”", (char)'`'), _scanTargetToken.startLocation, currentLocation);
                        return;
                    default:
                        _scanTargetToken.buffer.Append((char)_currentChar);
                        currentLocation.column++;
                        continue;
                }
            }

        }

        /// <summary>
        /// 扫描紧跟的小数部分。从 _currentChar 开始判断。
        /// </summary>
        private void scanFloatLiteral() {
            _scanTargetToken.buffer.Append('.');
            readDecimalDigits();
            currentLocation.column += _scanTargetToken.buffer.Length;
        }

        /// <summary>
        /// 扫描紧跟的空白字符。从 _currentChar 开始判断。
        /// </summary>
        private void scanWhiteSpace() {
            while (Unicode.isWhiteSpace(_currentChar)) {
                _currentChar = _input.Read();
                currentLocation.column++;
            }
        }

        /// <summary>
        /// 忽略当前行剩下的所有内容。从 _currentChar 开始判断。
        /// </summary>
        private void skipLine() {
            while (Unicode.isNonTerminator(_currentChar)) {
                _currentChar = _input.Read();
            }
        }

        /// <summary>
        /// 读取接下来的所有数字。从 _currentChar 开始判断。
        /// </summary>
        private void readDecimalDigits() {
            do {
                _scanTargetToken.buffer.Append((char)_currentChar);
            } while (Unicode.isDecimalDigit(_currentChar = _input.Read()));
        }

        /// <summary>
        /// 读取接下来的所有十六进制数字组成的字符。忽略 _currentChar 值。
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        private int readUnicodeChar(int length) {

            int result = 0;

            while (--length >= 0) {
                _currentChar = _input.Read();
                currentLocation.column++;
                int value = _currentChar - '0';
                if (value < 0 || value > 9) {
                    value = (_currentChar | 0x20) - 'a';
                    if (value < 0 || value > 5) {
                        Compiler.error(ErrorCode.expectedHexDight, "语法错误：应输入十六进制数字", currentLocation, currentLocation + 1);
                        return result;
                    }

                    value += 10;
                }

                result = (result << 4) | value;
            }

            return result;
        }

        /// <summary>
        /// 表示一个预编译指令。
        /// </summary>
        private enum PpDirective {

            /// <summary>
            /// #if true 指令。
            /// </summary>
            ifTrue,

            /// <summary>
            /// #if false 指令。
            /// </summary>
            ifFalse,

            /// <summary>
            /// #sub 指令。
            /// </summary>
            sub,

            /// <summary>
            /// #region 指令。
            /// </summary>
            region,

            /// <summary>
            /// #endregion 指令。
            /// </summary>
            endRegion,

            /// <summary>
            /// #endsub 指令。
            /// </summary>
            endSub,

            /// <summary>
            /// #endif 指令。
            /// </summary>
            endIf,

            /// <summary>
            /// #elseif true 指令。
            /// </summary>
            elseIfTrue,

            /// <summary>
            /// #elseif false 指令。
            /// </summary>
            elseIfFalse,

        }

        /// <summary>
        /// 当前的预编译指令栈。
        /// </summary>
        private Stack<PpDirective> _ppDirectiveStack = new Stack<PpDirective>();

        /// <summary>
        /// 检查指定的指令是否可用。
        /// </summary>
        /// <param name="errorCode"></param>
        /// <param name="name"></param>
        /// <param name="ppDirectives"></param>
        /// <returns></returns>
        private State checkPpDirective(ErrorCode errorCode, string name, PpDirective ppDirective1, PpDirective ppDirective2) {
            string expectedPpDirective;
            if (_ppDirectiveStack.Count == 0) {
                expectedPpDirective = "无效的 {0} 指令";
            } else {
                var peek = _ppDirectiveStack.Peek();
                if (ppDirective1 == peek) {
                    _ppDirectiveStack.Pop();
                    return State.on;
                }
                if (ppDirective2 == peek) {
                    _ppDirectiveStack.Pop();
                    return State.off;
                }
                switch (peek) {
                    case PpDirective.region:
                        expectedPpDirective = "无效的 {0} 指令；应输入 #endregion 标签";
                        break;
                    case PpDirective.ifFalse:
                    case PpDirective.ifTrue:
                        expectedPpDirective = "无效的 {0} 指令；应输入 #endif 标签";
                        break;
                    case PpDirective.sub:
                        expectedPpDirective = "无效的 {0} 指令；应输入 #endsub 标签";
                        break;
                    default:
                        throw new Unreachable();
                }
            }

            Compiler.error(errorCode, String.Format(expectedPpDirective, name), _scanTargetToken.startLocation, _scanTargetToken.endLocation);
            return State.unset;
        }

        /// <summary>
        /// 存储已定义的宏。
        /// </summary>
        Dictionary<string, Token> _subs;

        /// <summary>
        /// 确认当前行之后无其它内容。
        /// </summary>
        /// <param name="name"></param>
        private void checkPpNonthingLeft(string name) {
            if (tryPpScan() == State.on) {
                Compiler.error(ErrorCode.unexpectedPpDirectiveParams, String.Format("语法错误：#{0} 指令后不能有其它内容；应输入单行注释或换行", name), _scanTargetToken.startLocation, _scanTargetToken.endLocation);
                scanLine();
            }
        }

        /// <summary>
        /// 扫描紧跟的预编译指令参数。从 _currentChar 开始判断。
        /// </summary>
        private void scanPpArgument() {

            // #todo
            // #todo message

            _scanTargetToken.buffer.Insert(0, '#');
            int pos = _scanTargetToken.buffer.Length;
            scanWhiteSpace();
            scanLine();

            // 如果当前命名无任何参数，则更新位置。
            if (_scanTargetToken.buffer.Length == pos) {
                currentLocation.column = _scanTargetToken.startLocation.column + pos;
            } else {
                _scanTargetToken.buffer.Insert(pos, ' ');
            }
        }

        /// <summary>
        /// 解析并执行预处理表达式。
        /// </summary>
        /// <returns></returns>
        private bool parseAndEvalPpExpression() {

            #region 读取标记

            List<Token> tokens = new List<Token>();
            while (true) {
                switch (tryPpScan()) {
                    case State.on:
                        Token token;
                        switch (_scanTargetToken.type) {
                            case TokenType.identifier: {
                                    float val = Compiler.options.defines.Contains(_scanTargetToken.buffer.ToString()) ? 1f : 0f;
                                    token = createToken(val);
                                    break;
                                }
                            case TokenType.@true:
                                token = createToken(1f);
                                break;
                            case TokenType.@false:
                                token = createToken(0f);
                                break;
                            case TokenType.intLiteral:
                                token = createToken(parseLongToken(_scanTargetToken));
                                break;
                            case TokenType.hexIntLiteral:
                                token = createToken(parseHexIntToken(_scanTargetToken));
                                break;
                            case TokenType.floatLiteral:
                                token = createToken((float)parseFloatToken(_scanTargetToken));
                                break;
                            case TokenType.lParam:
                            case TokenType.rParam:
                            case TokenType.not:
                            case TokenType.sub:
                            case TokenType.logicalAnd:
                            case TokenType.logicalOr:
                            case TokenType.eq:
                            case TokenType.ne:
                            case TokenType.gt:
                            case TokenType.gte:
                            case TokenType.lt:
                            case TokenType.lte:
                                token = _scanTargetToken.clone();
                                break;
                            default:
                                Compiler.error(ErrorCode.invalidPpDirectiveExpression, String.Format("语法错误：无效预处理器表达式项：“{0}”", _scanTargetToken.type.getName()), _scanTargetToken.startLocation, _scanTargetToken.endLocation);
                                goto readError;
                        }
                        tokens.Add(token);
                        continue;
                    case State.off:
                        goto stopRead;
                    case State.unset:
                        goto readError;
                }
            }

        readError:
            skipLine();
            if (tokens.Count == 0) {
                return false;
            }

        stopRead:

            // 没有找到任何可用的表达式。
            if (tokens.Count == 0) {
                Compiler.error(ErrorCode.expectedPpDirectiveExpression, "语法错误：应输入表达式", currentLocation, currentLocation);
                return false;
            }

            #endregion

            #region 转回后缀表达式

            int tokenIndex = 0;
            Stack<Token> operators = new Stack<Token>();

            for (int i = 0; i < tokens.Count; i++) {
                Token token = tokens[i];
                switch (token.type) {
                    case TokenType.lParam:
                        operators.Push(token);
                        break;
                    case TokenType.rParam:
                        if (operators.Count == 0) {
                            Compiler.error(ErrorCode.unexpectedPpDirectiveExpressionRParam, "语法错误：多余的“)”", token.startLocation, token.endLocation);
                        }
                        while (operators.Count > 0) {
                            if (operators.Peek().type == TokenType.lParam) {
                                operators.Pop();
                                break;
                            }

                            tokens[tokenIndex++] = operators.Pop();
                        }
                        break;
                    case TokenType.floatLiteral:
                        tokens[tokenIndex++] = token;
                        break;
                    case TokenType.not:
                    case TokenType.sub:
                        operators.Push(token);
                        break;
                    default:

                        // 让优先级高的运算符先执行。
                        while (operators.Count > 0 && operators.Peek().type != TokenType.lParam && (operators.Peek().type.getPrecedence() <= token.type.getPrecedence() || operators.Peek().type == TokenType.sub || operators.Peek().type == TokenType.not)) {
                            tokens[tokenIndex++] = operators.Pop();
                        }

                        operators.Push(token);
                        break;
                }
            }

            // 中缀表达式全部读完后，若栈中仍有运算符，将其送到输出队列中。
            while (operators.Count > 0) {
                Token token = operators.Pop();
                if (token.type == TokenType.lParam) {
                    Compiler.error(ErrorCode.expectedPpDirectiveExpressionRParam, "语法错误：应输入“)”", tokens[tokens.Count - 1].startLocation, tokens[tokens.Count - 1].startLocation + 1);
                    continue;
                }
                tokens[tokenIndex++] = token;
            }

            #endregion

            #region 执行表达式

            Stack<float> values = new Stack<float>();
            for (int i = 0; i < tokenIndex; i++) {
                Token token = tokens[i];
                switch (token.type) {
                    case TokenType.floatLiteral:
                        values.Push(token.tag);
                        break;
                    case TokenType.not:
                        if (values.Count < 1) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() == 0f ? 1f : 0f);
                        break;
                    case TokenType.sub:
                        if (values.Count < 1) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(-values.Pop());
                        break;
                    case TokenType.logicalAnd:
                        if (values.Count < 2) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() != 0f && values.Pop() != 0f ? 1f : 0f);
                        break;
                    case TokenType.logicalOr:
                        if (values.Count < 2) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() != 0f || values.Pop() != 0f ? 1f : 0f);
                        break;
                    case TokenType.eq:
                        if (values.Count < 2) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() == values.Pop() ? 1f : 0f);
                        break;
                    case TokenType.ne:
                        if (values.Count < 2) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() != values.Pop() ? 1f : 0f);
                        break;
                    case TokenType.gt:
                        if (values.Count < 2) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() < values.Pop() ? 1f : 0f);
                        break;
                    case TokenType.gte:
                        if (values.Count < 2) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() <= values.Pop() ? 1f : 0f);
                        break;
                    case TokenType.lt:
                        if (values.Count < 2) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() > values.Pop() ? 1f : 0f);
                        break;
                    case TokenType.lte:
                        if (values.Count < 2) {
                            Compiler.error(ErrorCode.expectedPpDirectiveExpressionOperand, "语法错误：缺少操作数", token.startLocation, token.endLocation);
                            break;
                        }
                        values.Push(values.Pop() >= values.Pop() ? 1f : 0f);
                        break;
                }

            }

            return values.Count > 0 && values.Pop() != 0f;

            #endregion

        }

        private Token createToken(float value) {
            Token t = new Token();
            t.type = TokenType.floatLiteral;
            t.tag = value;
            t.startLocation = _scanTargetToken.startLocation;
            t.endLocation = _scanTargetToken.endLocation;
            return t;
        }

        /// <summary>
        /// 在处理预处理表达式中尝试读取下一个标记。
        /// </summary>
        /// <returns>如果成功读取到标记，则返回 State.on。如果无结果则返回 State.off。如果出现错误则返回 State.unset</returns>
        private State tryPpScan() {

            // 读取空白。
            scanWhiteSpace();

            // 读到行尾。
            if (Unicode.isLineTerminatorOrEOF(_currentChar)) {
                return State.off;
            }

            // 读到特殊符号。
            switch (_currentChar) {

                // 读到注释。
                case '/':
                    if (_input.Peek() == '/') {
                        skipLine();
                        return State.off;
                    }
                    if (_input.Peek() == '*') {
                        Compiler.error(ErrorCode.unexpectedPpDirectiveMutliLineComment, "语法错误：预处理表达式中只能使用单行注释", currentLocation, currentLocation + 2);
                        return State.unset;
                    }
                    goto case '#';

                // 读到其它预处理器。
                case '#':
                    Compiler.error(ErrorCode.unexpectedPpDirectiveParams, "语法错误：无效的预处理器表达式", currentLocation, currentLocation + 1);
                    return State.unset;

            }

            scan();
            return State.on;
        }

        /// <summary>
        /// 忽略读取到的标记，直到 #endif 或 #else。
        /// </summary>
        private void skipToPpElseOrEndIf() {

            do {
                scan();

                switch (_ppDirectiveStack.Peek()) {
                    case PpDirective.endIf:
                        _ppDirectiveStack.Pop();
                        scan();
                        return;
                    case PpDirective.elseIfTrue:
                        _ppDirectiveStack.Pop();
                        _ppDirectiveStack.Push(PpDirective.ifTrue);
                        scan();
                        return;
                    case PpDirective.elseIfFalse:
                        _ppDirectiveStack.Pop();
                        _ppDirectiveStack.Push(PpDirective.ifFalse);
                        continue;
                }

            } while (_scanTargetToken.type != TokenType.eof);

        }

        /// <summary>
        /// 报告当前标记不是标识符的错误。
        /// </summary>
        private void reportIsNotIdentifierError() {
            Compiler.error(ErrorCode.expectedIdentifier, _scanTargetToken.type.isKeyword() ? String.Format("语法错误：应输入标识符；“{0}”是关键字，请改为“${0}”", _scanTargetToken.type.getName()) : "语法错误：应输入标识符", _scanTargetToken.startLocation, _scanTargetToken.endLocation);
        }

        /// <summary>
        /// 扫描紧跟的一行字符。从 _currentChar 开始判断。
        /// </summary>
        private void scanLine() {

            // 读取非换行符。
            while (Unicode.isNonTerminator(_currentChar)) {
                _scanTargetToken.buffer.Append((char)_currentChar);
                _currentChar = _input.Read();
                currentLocation.column++;
            }

            // 忽略末尾空白。
            while (_scanTargetToken.buffer.Length > 0 && Unicode.isWhiteSpace(_scanTargetToken.buffer[_scanTargetToken.buffer.Length - 1])) {
                _scanTargetToken.buffer.Length--;
                currentLocation.column--;
            }

        }

        /// <summary>
        /// 解析指定标记中表示的整数。
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public static long parseLongToken(Token token) {
            long result = 0;
            try {
                for (int i = 0; i < token.buffer.Length; i++) {
                    result = checked(result * 10 + token.buffer[i] - '0');
                }
            } catch (OverflowException) {
                Compiler.error(ErrorCode.decimalNumberTooLarge, "整数常量值太大", token.startLocation, token.endLocation);
                result = long.MaxValue;
            }

            return result;
        }

        /// <summary>
        /// 解析指定标记中表示的十六进制整数。
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public static long parseHexIntToken(Token token) {
            long result = 0;
            try {
                for (int i = 0; i < token.buffer.Length; i++) {
                    if (Unicode.isDecimalDigit(token.buffer[i])) {
                        result = checked(result * 16 + token.buffer[i] - '0');
                    } else {
                        result = checked(result * 16 + 10 + (token.buffer[i] | 0x20) - 'a');
                    }
                }
            } catch (OverflowException) {
                Compiler.error(ErrorCode.hexNumberTooLarge, "整数常量值太大", token.startLocation, token.endLocation);
                result = long.MaxValue;
            }
            return result;
        }

        /// <summary>
        /// 解析指定标记中表示的浮点数。
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public static double parseFloatToken(Token token) {
            double num = 0;
            int i = 0;
            for (; i < token.buffer.Length; i++) {
                if (token.buffer[i] == '.') {
                    break;
                }
                num = num * 10 + token.buffer[i] - '0';
            }
            i++;
            for (int p = 10; i < token.buffer.Length; i++, p *= 10) {
                num += ((double)(token.buffer[i] - '0')) / p;
            }
            return num;
        }

        #endregion

    }

}